// LUA Gui Test Program
// Copyright 2004 (C) Ralph Thomas

//
// This is a little program which loads a Lua script that can use
// the OS's accessibility API to test GUIs. The Accessibility API
// is exposed to Lua by this program.
//
// We also expose a few useful OS functions, like sleep().
//

extern "C" {
#include <external/lua/include/lua.h>
#include <external/lua/include/lauxlib.h>
#include <external/lua/include/lualib.h>
}

#include <toolkit/access.h>
#include <iostream>

#ifndef floor
#include <math.h>
#endif

#ifdef _WINDOWS
#include <windows.h> // required for Sleep function
#define sleep( x ) Sleep( (x) * 1000 ) // Make it look like POSIX
#else
#include <unistd.h>
#endif

#define ERROR_NO_SCRIPT	"This program must be invoked with the name of a test\n script as it's argument. For example:\n  testGui testCalculator.lua"
#define ERROR_NO_LUA	"The Lua language runtime could not be loaded"
#define ERROR_NO_FILE	"The specified script file could not be loaded"
#define ERROR_CANT_RUN	"Could not run the specified script"
//
// struct accessUserData
//
/// This struct contains the access object. The object is wrapped in
/// a struct so that we can use the Lua memory allocator to give us
/// a userdata slot (which we then cast to this structure, and store
/// a pointer to an access object inside).
//
typedef struct {
	toolkit::access* a;
} accessUserData;

//
// toolkit::access* getAccessUserData( lua_State* L )
//
/// This function attempts to take some userdata from the first point
/// in the stack (where the first argument should be) and return it
/// as an accessUserData. If it isn't an accessUserData then NULL is
/// returned.
///
/// \param	L	the current Lua state, with argument stack
///
/// \return	either the toolkit::access object or NULL if it
///		couldn't be found on the stack.
//
toolkit::access* getAccessUserData( lua_State* L ) {
	//
	// The luaL_checkudata function will return a pointer to
	// userdata at the given stack position, and with the
	// specified metatable. Otherwise it returns NULL.
	//
	void* au = luaL_checkudata( L, 1, "access.mt" );
	//
	// The luaL_argcheck function checks to make sure the
	// userdata isn't NULL and throws a (longjmp) Lua
	// exception if it is.
	//
	luaL_argcheck( L, au != NULL, 1, "`access' object expected" );
	//
	// We're still here, so it is safe to assume that the
	// value in au is actually an accessUserData structure.
	//
	return ((accessUserData*)au)->a;
}
//
// bool createLuaAccess( lua_State* L, toolkit::access* a )
//
/// This function creates a new Lua toolkit::access object and places
/// it at the end of the stack.
///
/// \param	L	the Lua stack and state
/// \param	a	the access object to wrap
///
/// \return	true	the object was wrapped
///		false	the object could not be wrapped
//
bool createLuaAccess( lua_State* L, toolkit::access* a ) {
	//
	// We must make sure we have both a valid access object and
	// a valid Lua pointer.
	//
	if( !L || !a ) return false;
	//
	// We have a valid access object. We make a user data for it
	// in Lua. We will associate a metatable with this userdata
	// which defines all of the functions which can be used on
	// an access object from Lua.
	//
	accessUserData* au = (accessUserData*)lua_newuserdata( L, sizeof( accessUserData ) );
	if( !au ) return false; // no memory
	//
	// Put our access object into the userdata space we allocated for it.
	//
	au->a = a;
	//
	// Now we can assign our metatable to our userdata.
	//
	luaL_getmetatable( L, "access.mt" );
	lua_setmetatable( L, -2 );
	return true;
}
//
// int l_getName( lua_State* L )
//
/// This function uses the toolkit::access::getName method to find out
/// the name of some access object. It expects an access metatable (as
/// created by l_findWindow) to be on the stack as an argument.
///
/// \param	L	the Lua state and stack with access metatable
///
/// \return	the number of return arguments on the stack, always
///		one for this function. The return argument is a string.
//
static int l_getName( lua_State* L ) {
	//
	// We're expecting our arguments to look like this:
	//  1. toolkit::access object
	//
	toolkit::access* a = getAccessUserData( L );
	if( !a ) {
		lua_pushnil( L );
		return 1;
	}
	//
	// Get the name of our access object
	//
	util::string name = a->getName();
	//
	// Push the name into Lua
	//
	lua_pushfstring( L, name.c_str(), name.size() );
	//
	// Return to Lualand
	//
	return 1;
}
//
// int l_getDimensions( lua_State* L )
//
/// This function uses the toolkit::access::getDimensions function to
/// create a table containing the dimensions of our access object.
///
/// \param	L	the stack of Lua, including our arguments
///
/// \return	the number of return arguments on the stack, always 1.
//
static int l_getDimensions( lua_State* L ) {
	//
	// We're expecting our arguments to look like thid:
	//  1. toolkit::access object
	//
	toolkit::access* a = getAccessUserData( L );
	if( !a ) {
		lua_pushnil( L );
		return 1;
	}
	//
	// Get the dimensions of the access object
	//
	toolkit::rect r = a->getDimensions();
	//
	// We return this data to Lua as a table. The table has
	// "x", "y", "width" and "height" members.
	//
	lua_newtable( L );
	//
	// Put the x value in first
	//
	lua_pushstring( L, "x" );
	lua_pushnumber( L, r.x );
	lua_settable( L, -3 );
	//
	// Now y
	//
	lua_pushstring( L, "y" );
	lua_pushnumber( L, r.y );
	lua_settable( L, -3 );
	//
	// Width
	//
	lua_pushstring( L, "width" );
	lua_pushnumber( L, r.width );
	lua_settable( L, -3 );
	//
	// Height
	//
	lua_pushstring( L, "height" );
	lua_pushnumber( L, r.height );
	lua_settable( L, -3 );
	//
	// Now our rectangle is in the table at the end of the stack
	// we can return it all to Lua.
	//
	return 1;
}
//
// int l_sendText( lua_State* L )
//
/// This function uses the toolkit::access::sendText method to send
/// text to an access object. It has no return value.
///
/// \param	L	the Lua stack, including our arguments.
///
/// \return	This function always returns 1, but only the nil value
///		is added to the stack.
//
static int l_sendText( lua_State* L ) {
	//
	// We expect our arguments to look like this:
	//  1. toolkit::access object
	//  2. text to send
	//
	toolkit::access* a = getAccessUserData( L );
	const char* text = lua_tostring( L, 2 );
	//
	// This function always returns nil regardless of what happens,
	// so we may as well push it now.
	//
	lua_pushnil( L );
	//
	// If either "a" or "text" are NULL then there's not much we
	// can do.
	//
	if( !a || !text ) return 1;
	//
	// We can now attempt to send the text to our widget.
	//
	a->sendText( text );
	//
	// Return nil
	//
	return 1;
}
//
// int l_sendClick( lua_State* L )
//
/// This function uses the toolkit::access::sendClick method to send
/// a mouse click event to an access object. It has no return value.
///
/// \param	L	the Lua stack, including ourselves
///
/// \return	This functions always returns 1, but only the nil value
///		is added to the stack.
//
static int l_sendClick( lua_State* L ) {
	toolkit::access* a = getAccessUserData( L );
	lua_pushnil( L );
	if( !a ) return 1;
	a->sendClick();
	return 1;
}
//
// int l_sendClose( lua_State* L )
//
/// This function sends a close message to the access object, using the
/// toolkit::access::sendClose() method. It has no return value.
///
/// \param	L	the Lua stack.
///
/// \return	1, which is for the nil value pushed onto the stack.
//
static int l_sendClose( lua_State* L ) {
	toolkit::access* a = getAccessUserData( L );

	lua_pushnil( L );
	if( !a ) return 1;
	a->sendClose();
	return 1;
}
//
// int l_getNumberChildren( lua_State* L )
//
/// This function returns the number of children an access object has.
/// It has a number return value, which is nil in the case that the
/// accessobject can't be extracted from Lua properly.
///
/// \param	L	the Lua stack
///
/// \return	1, which is either for the number on the stack, or for nil
///		if there was an error.
//
static int l_getNumberChildren( lua_State* L ) {
	toolkit::access* a = getAccessUserData( L );

	if( !a ) {
		lua_pushnil( L );
		return 1;
	}
	lua_pushnumber( L, a->getNumberChildren() );
	return 1;
}
//
// int l_getChild( lua_State* L )
//
/// This function doesn't work until there's some kind of "copy" method
/// in toolkit::access.
///
/// \param	L	the Lua stack
///
/// \return	1, which is for a nil value pushed onto the stack.
//
static int l_getChild( lua_State* L ) {
	//
	// We are expecting our arguments to be:
	//  1. toolkit::access object
	//  2. the index of the child to get
	//
	toolkit::access* a = getAccessUserData( L );
	int i = (int)floor( lua_tonumber( L, 2 ) );
	//
	// We need to make sure that we have a valid toolkit object,
	// and that we have an actual number for our index.
	//
	if( !a || !lua_isnumber( L, 2 ) ) {
		//
		// Doh - either we don't have a toolkit::access object
		// or we don't have an index.
		//
		lua_pushnil( L );
		return 1;
	}
	//
	// We can now try to get the child.
	//
	toolkit::access* ch = a->getChild( i );
	if( !ch ) {
		//
		// We couldn't get that child. We need to give nil back
		// to Lua.
		//
		lua_pushnil( L );
		return 1;
	}
	if( !createLuaAccess( L, ch ) ) {
		//
		// We couldn't create a Lua table wrapper object for this
		// access object.
		//
		delete ch;
		lua_pushnil( L );
		return 1;
	}
	//
	// All is well, and createLuaAccess put the new table onto the
	// return stack for us.
	//
	return 1;
}
//
// int l_destructor( lua_State* L )
//
/// This function gets called by Lua's garbage collector. We take the
/// opportunity to delete the access object.
///
/// \param	L	the Lua stack
///
/// \return	1, which is for a nil value.
//
static int l_destructor( lua_State* L ) {
	toolkit::access* a = getAccessUserData( L );
	//
	// If we do have an access object then we need to delete
	// it.
	//
	if( a ) delete a;
	//
	// Return nil to Lua.
	//
	lua_pushnil( L );
	return 1;
}
//
// int l_findWindow( lua_State* L )
//
/// This function uses the toolkit::access::fromWindowName to get an
/// access object. If the window was found then it creates a table
/// and puts accessor methods in to it. The table can then be used
/// from Lua code to do all the things which the toolkit::access
/// interface allows.
///
/// \param	L	the Lua state and stack
///
/// \return	the number of return arguments on the stack, always
///		one for this function.
//
static int l_findWindow( lua_State* L ) {
	//
	// We need to get a string argument off of the stack. This is the
	// name of the window we need to find.
	//
	const char* window = lua_tostring( L, 1 );
	//
	// If we got NULL back from tostring then there was either no
	// argument, or the argument couldn't be made a string. We need
	// to bail.
	//
	if( !window ) {
		lua_pushnil( L );
		return 1;
	}
	//
	// Now we're ready to try and find the window, using the
	// toolkit::access::fromWindowName method.
	//
	toolkit::access* a = toolkit::access::fromWindowName( window );
	//
	// If "a" is NULL, then there was no window of that name on the
	// screen. We should return nil here. A future extension might
	// be to wait for some time for a window of that name to be
	// created.
	//
	if( !a || !createLuaAccess( L, a ) ) {
		lua_pushnil( L );
		return 1;
	}
	return 1;
}
//
// int l_sleep( lua_State* L )
//
/// This function unpacks an integer from the Lua stack, and then sleeps
/// for that many seconds before returning to Lua.
///
/// \param	L	the Lua state and stack
///
/// \return	Always one (this function returns nil).
//
static int l_sleep( lua_State* L ) {
	//
	// We need to get the number of seconds to sleep off of the stack.
	//
	int s = (int)lua_tonumber( L, 1 );
	//
	// Make sure that this value is actually within range.
	//
	if( s < 1 ) { lua_pushnil( L ); return 1; }
	//
	// Sleep some.
	//
	sleep( s );
	//
	// Return to Lua.
	//
	lua_pushnil( L );
	return 1;
}

//
/// This structure defines the single entrypoint into the toolkit::access
/// functionality.
//
static const struct luaL_reg accessLibStaticFunctions [] = {
	{ "findWindow", l_findWindow },
	{ "sleep", l_sleep },
	{ NULL, NULL }
};
//
/// This structure defines all of the method names which can be used
/// from Lua on access object which we return.
//
static const struct luaL_reg accessLibAccessMethods[] = {
	{ "name", l_getName },
	{ "dimensions", l_getDimensions },
	{ "sendText", l_sendText },
	{ "sendClick", l_sendClick },
	{ "sendClose", l_sendClose },
	{ "numberChildren", l_getNumberChildren },
	{ "child", l_getChild },
	{ "__gc", l_destructor },
	{ NULL, NULL }
};
//
// int luaopen_accesslib( lua_State* L )
//
/// This function opens the access library for use in Lua code. It creates
/// the metatable which defines all of the function entrypoints for the
/// access objects which get returned by findWindow into Lua space.
///
/// \param	L	the Lua state and stack
///
/// \return	this function always returns 1 (true).
//
int luaopen_accesslib( lua_State* L ) {
	//
	// We call our new table "access.mt"
	//
	luaL_newmetatable( L, "access.mt" );
	//
	// We make the metatable's index function be itself. This means
	// that the metatable acts as an index for regular tables which
	// have it set as their metatable.
	//
	lua_pushstring( L, "__index" );
	//
	// Push the metatable itself to the top of the stack. Then
	// settable will set access.mt.__index = access.mt;
	//
	lua_pushvalue( L, -2 );
	//
	// Do the assignment "access.mt.__index = access.mt".
	//
	lua_settable( L, -3 );
	//
	// Calling luaL_openlib with a NULL library name causes it to add
	// the functions to the table which is currently on the top of
	// the stack. In this case that table is our metatable, and we
	// want those methods in there anyway.
	//
	luaL_openlib( L, NULL, accessLibAccessMethods, 0 );
	//
	// Now we can add the regular "static" functions to the Lua runtime.
	//
	luaL_openlib( L, "accesslib", accessLibStaticFunctions, 0 );
	//
	// This function always returns success
	//
	return 1;
}

int main( int argc, char* argv[] ) {
	//
	// We need to make sure that we were invoked with the a script
	// that we can run.
	//
	if( argc < 2 ) {
		std::cerr << ERROR_NO_SCRIPT << std::endl;
		return 1;
	}
	const char* script = argv[1];
	//
	// Now we can startup Lua
	//
	lua_State* L = lua_open();
	if( !L ) {
		std::cerr << ERROR_NO_LUA << std::endl;
		return 1;
	}
	//
	// Open the lua libraries
	//
	luaopen_base( L );
	luaopen_table( L );
	luaopen_io( L );
	luaopen_string( L );
	luaopen_math( L );
	//
	// Lua is ready to use now. We need to add our testing functions
	// to it (so that our test scripts will have access to the
	// Accessibility API).
	//
	luaopen_accesslib( L );
	//
	// Now we can open the script file we were given as an argument
	// and start running it.
	//
	if( luaL_loadfile( L, script ) ) {
		std::cerr << ERROR_NO_FILE << " - ";
		std::cerr << lua_tostring( L, -1 ) << std::endl;
		lua_close( L );
		return 1;
	}
	//
	// Run the script
	//
	if( lua_pcall( L, 0, 0, 0 ) ) {
		std::cerr << ERROR_CANT_RUN << " - ";
		std::cerr << lua_tostring( L, -1 ) << std::endl;
		lua_close( L );
		return 1;
	}
	//
	// For now, we're done
	//
	lua_close( L );
	return 0;
}
